package com.geek.projects.utils;


import com.geek.projects.annotation.TableField;
import com.geek.projects.annotation.TableName;
import com.geek.projects.function.ThrowableFunction;
import org.apache.commons.lang.ClassUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.derby.iapi.util.StringUtil;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;


/**
 * 简单数据处理工具类
 *
 * @author lizhiyang
 */
public class RepositoryHandleUtil {


    public static <T> List<T> handleResultSetList(T t, ResultSet resultSet) throws Exception {
        BeanInfo userBeanInfo = Introspector.getBeanInfo(t.getClass(), Object.class);
        List<T> list = new ArrayList<>();
        while (resultSet.next()) {
            for (PropertyDescriptor propertyDescriptor : userBeanInfo.getPropertyDescriptors()) {
                String fieldName = propertyDescriptor.getName();
                Class<?> fieldType = propertyDescriptor.getPropertyType();
                String methodName = resultSetMethodMappings.get(fieldType);
                // 可能存在映射关系（不过此处是相等的）
                String columnLabel = mapColumnLabel(fieldName);
                Method resultSetMethod = ResultSet.class.getMethod(methodName, String.class);
                // 通过放射调用 getXXX(String) 方法
                Object resultValue = resultSetMethod.invoke(resultSet, columnLabel);
                Method writeMethod = propertyDescriptor.getWriteMethod();
                writeMethod.invoke(t, resultValue);
                list.add(t);
            }
        }
        return list;
    }

    public static <T> Integer executeInsert(Consumer<Throwable> exceptionHandler,
                                            Connection connection, T t) {
        try {
            Class<?> aClass = t.getClass();
            TableName tableName = aClass.getAnnotation(TableName.class);
            StringBuilder sql = new StringBuilder(" INSERT INTO  ")
                    .append(tableName.name())
                    .append(" (REPLACE1) ")
                    .append(" VALUES ")
                    .append(" (REPLACE2)");

            Field[] declaredFields = aClass.getDeclaredFields();
            StringBuilder sql2 = new StringBuilder();
            StringBuilder sql3 = new StringBuilder();
            for (Field declaredField : declaredFields) {
                TableField annotation = declaredField.getAnnotation(TableField.class);
                if (annotation != null) {
                    String name = declaredField.getName();
                    sql2.append(name).append(",");
                    declaredField.setAccessible(true);
                    Object o = declaredField.get(t);
                    sql3.append("'").append(o).append("',");
                }
            }
            String newSql = StringUtils.replace(sql.toString(),
                    "REPLACE1", sql2.deleteCharAt(sql2.length() - 1).toString())
                    .replace("REPLACE2", sql3.deleteCharAt(sql3.length() - 1).toString());
            Statement statement = connection.createStatement();
            return statement.executeUpdate(newSql);
        } catch (Exception e) {
            exceptionHandler.accept(e);
            throw new RuntimeException(e);
        }
    }


    public static <T> T executeQuery(String sql,
                                     ThrowableFunction<ResultSet, T> function,
                                     Consumer<Throwable> exceptionHandler,
                                     Connection connection, Object... args) {
        try {
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                Object arg = args[i];
                Class<?> argType = arg.getClass();
                Class<?> wrapperType = ClassUtils.wrapperToPrimitive(argType);
                if (wrapperType == null) {
                    wrapperType = argType;
                }
                String methodName = preparedStatementMethodMappings.get(wrapperType);
                Method method = PreparedStatement.class.getMethod(methodName, int.class, wrapperType);
                method.invoke(preparedStatement, i + 1, arg);
            }
            ResultSet resultSet = preparedStatement.executeQuery();
            return function.apply(resultSet);
        } catch (Throwable e) {
            e.printStackTrace();
            exceptionHandler.accept(e);
            throw new RuntimeException(e);
        }
    }

    private static String mapColumnLabel(String fieldName) {
        return fieldName;
    }

    /**
     * 数据类型与 ResultSet 方法名映射
     */
    private static Map<Class, String> resultSetMethodMappings = new HashMap<>();

    private static Map<Class, String> preparedStatementMethodMappings = new HashMap<>();

    static {
        resultSetMethodMappings.put(Long.class, "getLong");
        resultSetMethodMappings.put(String.class, "getString");

        preparedStatementMethodMappings.put(Long.class, "setLong"); // long
        preparedStatementMethodMappings.put(String.class, "setString"); //


    }
}
